package com.keqiang.chart.impl.axis.render

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PathEffect
import android.graphics.RectF
import android.text.Layout
import android.text.TextPaint
import com.keqiang.chart.impl.axis.BaseAxisChart
import com.keqiang.chart.impl.axis.BaseYAxis
import com.keqiang.chart.impl.axis.Label
import com.keqiang.chart.utils.ObjectPool
import com.keqiang.utils.PaintUtils

/**
 * Y轴绘制器
 *
 * @param drawLabelByGridLine 标签是否与网格线垂直居中对齐，如果为false，则标签与网格中心点垂直居中对齐
 *
 * @author Created by wanggaowan on 2022/7/20 09:10
 */
open class YAxisRender<T : BaseAxisChart<*>> @JvmOverloads constructor(
    val chart: T, val drawLabelByGridLine: Boolean = true) : IYAxisRender {

    override fun onDraw(canvas: Canvas,
                        start: Float, top: Float,
                        end: Float, bottom: Float,
                        labelMaxWidth: Float,
                        gridHeight: Float,
                        xAxisRange: Float) {

        val paint = ObjectPool.obtainPaint<Paint>()
        val textPaint = ObjectPool.obtainPaint<TextPaint>(true)
        val tempRectF = ObjectPool.obtainRectF()
        val path = ObjectPool.obtainPath()

        // 绘制轴线
        val yAxis = chart.yAxis
        val xAxis = chart.xAxis
        setPaint(paint, yAxis.lineColor, yAxis.lineWidth, yAxis.linePathEffect)
        val x = start + (end - start) / 2f
        path.moveTo(x, top)
        path.lineTo(x, bottom)
        canvas.drawPath(path, paint)
        yAxis.drawRectF.set(start, top, end, bottom)

        // 绘制y轴文本
        var tempBottom = bottom - yAxis.startPadding
        textPaint.textSize = yAxis.labelTextSize
        textPaint.color = yAxis.labelColor
        setPaint(paint, yAxis.gridLineColor, yAxis.gridLineWidth, yAxis.gridLinePathEffect)

        yAxis.labelList?.let {
            for (index in it.indices) {
                if (labelMaxWidth > 0 && gridHeight > 0) {
                    if (drawLabelByGridLine) {
                        drawLabelByGridLine(canvas, textPaint, index, it, yAxis, gridHeight, start, labelMaxWidth, tempBottom, tempRectF)
                    } else {
                        drawLabelByGridCenter(canvas, textPaint, index, it, yAxis, gridHeight, start, labelMaxWidth, tempBottom, tempRectF)
                    }
                }

                if (index > 0 && yAxis.drawGridLine) {
                    var stopX =
                        if (yAxis.gridUseEndPadding) start + xAxisRange - xAxis.endPadding else start + xAxisRange
                    if (xAxis.drawGridLine && index == it.size - 1) {
                        // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
                        stopX += xAxis.gridLineWidth / 2
                    }

                    path.reset()
                    path.moveTo(if (yAxis.gridUseStartPadding) start + xAxis.startPadding else start, tempBottom)
                    path.lineTo(stopX, tempBottom)
                    canvas.drawPath(path, paint)
                }

                tempBottom -= gridHeight
            }

            if (!drawLabelByGridLine && yAxis.drawGridLine) {
                var stopX =
                    if (yAxis.gridUseEndPadding) start + xAxisRange - xAxis.endPadding else start + xAxisRange
                if (xAxis.drawGridLine) {
                    // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
                    stopX += xAxis.gridLineWidth / 2
                }

                path.reset()
                path.moveTo(if (yAxis.gridUseStartPadding) start + xAxis.startPadding else start, tempBottom)
                path.lineTo(stopX, tempBottom)
                canvas.drawPath(path, paint)
            }
        }

        ObjectPool.recycleRectF(tempRectF)
        ObjectPool.recyclePaint(paint)
        ObjectPool.recyclePaint(textPaint)
        ObjectPool.recyclePath(path)
    }

    /**
     * 绘制标签，内容与网格线水平居中对齐
     */
    protected open fun drawLabelByGridLine(canvas: Canvas, textPaint: TextPaint, index: Int, it: List<Label>, yAxis: BaseYAxis,
                                           gridHeight: Float, start: Float, labelMaxWidth: Float, tempBottom: Float, tempRectF: RectF,
                                           isReverse: Boolean = false) {
        val label = it[index]
        val maxHeight = gridHeight * label.weight
        if (maxHeight <= yAxis.labelVerticalPadding) {
            return
        }

        val textStart =
            if (!isReverse) start - yAxis.labelOffsetAxis - labelMaxWidth else start + yAxis.labelOffsetAxis
        val textEnd = if (!isReverse) start - yAxis.labelOffsetAxis else textStart + labelMaxWidth

        // 起始标签与第二个标签之间的间隔由其它标签承担，因为起始标签高度只有网格高度的一半
        val offsetVag =
            yAxis.labelVerticalPadding / 2f / (if (yAxis.labelCount <= 2) 1 else yAxis.labelCount - 1)
        val text = label.value
        if (!text.isNullOrEmpty()) {
            var labelAlign = yAxis.labelAlign
            val overAlign = yAxis.labelOverAlign
            if (yAxis.labelMaxLines > 1 && overAlign != labelAlign) {
                if (PaintUtils.getTextLine(text, textPaint, labelMaxWidth.toInt()) > 1) {
                    labelAlign = overAlign
                }
            }

            val textHeight =
                PaintUtils.getTextHeight(textPaint, text, labelMaxWidth.toInt(), yAxis.labelMaxLines)

            canvas.save()

            when (index) {
                0 -> {
                    var bottom: Float
                    if (textHeight > maxHeight / 2) {
                        bottom = tempBottom + yAxis.startPadding
                        if (bottom > textHeight) {
                            bottom = textHeight.toFloat()
                        }
                    } else {
                        bottom = tempBottom
                    }

                    tempRectF.set(textStart, tempBottom - maxHeight / 2f,
                        textEnd, bottom)
                    canvas.clipRect(tempRectF)
                    val topY = if (textHeight > tempRectF.height()) {
                        tempRectF.top
                    } else {
                        tempRectF.bottom - textHeight
                    }
                    canvas.translate(tempRectF.left, topY)
                }

                else -> {
                    val topY: Float
                    if (index == it.size - 1) {
                        // 最后一个标签与倒数第二个标签中间到x轴轴线结束位置
                        // 可绘制区域下半部分高度
                        val bottomHalf =
                            gridHeight / 2f - yAxis.labelVerticalPadding / 2f - offsetVag
                        val textHeightHalf = textHeight / 2f
                        topY =
                            if (textHeightHalf <= yAxis.endPadding && textHeightHalf <= bottomHalf) {
                                tempRectF.set(textStart, tempBottom - textHeightHalf, textEnd, tempBottom + textHeightHalf)
                                tempRectF.top
                            } else {
                                tempRectF.set(textStart, tempBottom - yAxis.endPadding, textEnd, tempBottom + bottomHalf)
                                if (textHeight > tempRectF.height() || yAxis.endPadding < textHeightHalf) {
                                    tempRectF.top
                                } else {
                                    tempRectF.top + tempRectF.height() / 2 - textHeightHalf
                                }
                            }
                    } else {
                        val maxTextHeight =
                            maxHeight - yAxis.labelVerticalPadding - offsetVag
                        tempRectF.set(textStart, tempBottom - maxTextHeight / 2f,
                            textEnd, tempBottom + maxTextHeight / 2f)
                        topY = if (textHeight > tempRectF.height()) {
                            tempRectF.top
                        } else {
                            tempRectF.top + tempRectF.height() / 2 - textHeight / 2f
                        }
                    }
                    canvas.clipRect(tempRectF)
                    canvas.translate(tempRectF.left, topY)
                }
            }

            val align = when (labelAlign) {
                Paint.Align.CENTER -> Layout.Alignment.ALIGN_CENTER
                Paint.Align.LEFT -> Layout.Alignment.ALIGN_NORMAL
                else -> Layout.Alignment.ALIGN_OPPOSITE
            }
            PaintUtils.drawText(canvas, textPaint, text, labelMaxWidth.toInt(), yAxis.labelMaxLines, align, yAxis.labelEllipsize)

            canvas.restore()
        }
    }

    /**
     * 绘制标签，内容与网格中点水平居中对齐
     */
    protected open fun drawLabelByGridCenter(
        canvas: Canvas, textPaint: TextPaint, index: Int, it: List<Label>, yAxis: BaseYAxis,
        gridHeight: Float, start: Float, labelMaxWidth: Float, tempBottom: Float, tempRectF: RectF,
        isReverse: Boolean = false) {
        val label = it[index]
        val maxHeight = gridHeight * label.weight
        if (maxHeight <= yAxis.labelVerticalPadding) {
            return
        }

        val textStart =
            if (!isReverse) start - yAxis.labelOffsetAxis - labelMaxWidth else start + yAxis.labelOffsetAxis
        val textEnd = if (!isReverse) start - yAxis.labelOffsetAxis else textStart + labelMaxWidth

        val text = label.value
        if (!text.isNullOrEmpty()) {
            var labelAlign = yAxis.labelAlign
            val overAlign = yAxis.labelOverAlign
            if (yAxis.labelMaxLines > 1 && overAlign != labelAlign) {
                if (PaintUtils.getTextLine(text, textPaint, labelMaxWidth.toInt()) > 1) {
                    labelAlign = overAlign
                }
            }

            val textHeight =
                PaintUtils.getTextHeight(textPaint, text, labelMaxWidth.toInt(), yAxis.labelMaxLines)

            canvas.save()

            // 相比gridHeight通过权重扩充/缩小的高度
            val overHeight = gridHeight * label.weight - gridHeight
            tempRectF.set(textStart, tempBottom - gridHeight - overHeight / 2f + yAxis.labelVerticalPadding / 2f,
                textEnd, tempBottom + overHeight / 2f - yAxis.labelVerticalPadding / 2f)
            val topY = if (textHeight > tempRectF.height()) {
                tempRectF.top
            } else {
                tempRectF.top + tempRectF.height() / 2 - textHeight / 2f
            }
            canvas.clipRect(tempRectF)
            canvas.translate(tempRectF.left, topY)

            val align = when (labelAlign) {
                Paint.Align.CENTER -> Layout.Alignment.ALIGN_CENTER
                Paint.Align.LEFT -> Layout.Alignment.ALIGN_NORMAL
                else -> Layout.Alignment.ALIGN_OPPOSITE
            }
            PaintUtils.drawText(canvas, textPaint, text, labelMaxWidth.toInt(), yAxis.labelMaxLines, align, yAxis.labelEllipsize)

            canvas.restore()
        }
    }

    protected fun setPaint(paint: Paint, color: Int, strokeWidth: Float = 0f, pathEffect: PathEffect? = null) {
        paint.color = color
        paint.strokeWidth = strokeWidth
        paint.style = Paint.Style.STROKE
        paint.pathEffect = pathEffect
    }
}
